package com.example.demo.client;

import com.example.demo.config.KookProperties;
import com.example.demo.handler.MessageHandler;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.Timer;
import java.util.TimerTask;
import java.util.zip.InflaterInputStream;

@Slf4j
public class KookWebSocketClient extends WebSocketClient {

    public interface ReconnectAware {
        void onReconnectRequired();
    }

    private final ReconnectAware reconnectAware;

    private final ObjectMapper objectMapper;
    private final KookProperties kookProperties;
    private final MessageHandler messageHandler;
    private Timer heartbeatTimer;
    private String sessionId;
    private int sn;
    private long lastPongTime;

    public KookWebSocketClient(URI serverUri, KookProperties kookProperties, ObjectMapper objectMapper, MessageHandler messageHandler, ReconnectAware reconnectAware) {
        super(serverUri);
        this.kookProperties = kookProperties;
        this.objectMapper = objectMapper;
        this.messageHandler = messageHandler;
        this.reconnectAware = reconnectAware;
    }

    private Timer helloTimeoutTimer;
    private static final int HELLO_TIMEOUT = 6000; // 6秒超时

    private volatile boolean isConnecting = false;
    private static final int CONNECTION_TIMEOUT = 5000; // 5秒连接超时
    private Timer connectionTimeoutTimer;

    @Override
    public void onOpen(ServerHandshake handshakedata) {
        log.info("正在连接到KOOK服务器...");
        isConnecting = true;

        if (kookProperties.getWebsocket().getCompress() == 1) {
            log.debug("已启用消息压缩");
        }

        // 启动连接超时检测
        connectionTimeoutTimer = new Timer();
        connectionTimeoutTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                if (isConnecting) {
                    log.error("连接KOOK服务器超时");
                    handleReconnect();
                }
            }
        }, CONNECTION_TIMEOUT);

        // 启动hello包超时检测
        helloTimeoutTimer = new Timer();
        helloTimeoutTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                if (!hasReceivedHello && isConnecting) {
                    log.error("等待服务器响应超时");
                    handleReconnect();
                }
            }
        }, HELLO_TIMEOUT);
    }

    @Override
    public void onMessage(String message) {
        processMessage(message);
    }

    @Override
    public void onMessage(ByteBuffer bytes) {
        try {
            // 解压缩消息
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ByteArrayInputStream in = new ByteArrayInputStream(bytes.array());
            InflaterInputStream inflater = new InflaterInputStream(in);
            byte[] buffer = new byte[1024];
            int length;
            while ((length = inflater.read(buffer)) > 0) {
                out.write(buffer, 0, length);
            }
            String message = new String(out.toByteArray(), "UTF-8");
            processMessage(message);
        } catch (Exception e) {
            log.error("解压消息失败", e);
        }
    }

    private void processMessage(String message) {
        try {
            // 仅在调试时输出原始消息
            log.debug("收到原始消息: {}", message);
            JsonNode jsonNode = objectMapper.readTree(message);
            int signal = jsonNode.get("s").asInt();
            
            switch (signal) {
                case 1: // hello
                    handleHello(jsonNode);
                    break;
                case 0: // event
                    handleEvent(jsonNode);
                    break;
                case 3: // pong
                    long currentTime = System.currentTimeMillis();
                    long interval = currentTime - lastPongTime;
                    lastPongTime = currentTime;
                    log.debug("收到pong响应，心跳间隔: {}ms", interval);
                    break;
                case 5: // reconnect
                    log.info("收到服务器重连请求");
                    handleReconnect();
                    break;
                default:
                    log.info("收到未处理的信号类型: {}, 消息内容: {}", signal, message);
            }
        } catch (Exception e) {
            log.error("处理消息时发生错误，消息内容: {}", message, e);
        }
    }

    private void handleHello(JsonNode jsonNode) {
        clearTimers();
        isConnecting = false;
        JsonNode data = jsonNode.get("d");
        int code = data.get("code").asInt();
        
        if (code == 0) {
            sessionId = data.get("session_id").asText();
            hasReceivedHello = true;
            startHeartbeat();
            log.info("已成功连接到KOOK服务器");
        } else {
            log.error("连接KOOK服务器失败，错误码: {}", code);
            close();
        }
    }

    private void handleEvent(JsonNode jsonNode) {
        try {
            // 更新消息序号
            this.sn = jsonNode.get("sn").asInt();
            log.debug("更新消息序号: {}", this.sn);
            
            // 处理具体的事件消息
            JsonNode eventData = jsonNode.get("d");
            String type = eventData.get("type").asText();
            log.debug("收到事件消息，类型: {}", type);
            
            switch (type) {
                case "1": // 文字消息
                case "9": // 命令消息
                    handleTextMessage(eventData);
                    break;
                case "255": // 系统事件
                    JsonNode extraNode = eventData.get("extra");
                    if (extraNode != null && extraNode.get("type") != null) {
                        String eventType = extraNode.get("type").asText();
                        log.info("系统事件：{}", eventType);
                        messageHandler.handleTextMessage(eventData, kookProperties.getToken());
                    } else {
                        log.warn("收到系统事件但缺少必要字段：{}", eventData);
                    }
                    break;
                // 可以添加其他类型的消息处理
                default:
                    log.debug("未处理的消息类型: {}", type);
            }
        } catch (Exception e) {
            log.error("处理事件消息时发生错误，消息内容: {}", jsonNode, e);
        }
    }

    private void handleTextMessage(JsonNode eventData) {
        try {
            JsonNode contentNode = eventData.get("content");
            JsonNode targetIdNode = eventData.get("target_id");
            JsonNode authorIdNode = eventData.get("author_id");
            JsonNode extraNode = eventData.get("extra");
            
            if (contentNode == null || targetIdNode == null || authorIdNode == null || extraNode == null) {
                log.warn("消息缺少必要字段：{}", eventData);
                return;
            }
            
            JsonNode authorNode = extraNode.get("author");
            if (authorNode == null || authorNode.get("username") == null) {
                log.warn("消息缺少作者信息：{}", eventData);
                return;
            }
            
            String content = contentNode.asText();
            String channelId = targetIdNode.asText();
            String authorId = authorIdNode.asText();
            String username = authorNode.get("username").asText();
            String channelName = extraNode.get("channel_name") != null ? 
                extraNode.get("channel_name").asText() : "未知频道";
            
            log.info("[{}] {} 说：{}", channelName, username, content);
            
            // 使用消息处理器处理消息
            messageHandler.handleTextMessage(eventData, kookProperties.getToken());
            log.debug("消息处理完成");
        } catch (Exception e) {
            log.error("处理文本消息时发生错误，消息内容: {}", eventData, e);
        }
    }

    private void handleReconnect() {
        log.info("正在重新连接到KOOK服务器...");
        clearTimers();
        
        // 重置所有状态
        hasReceivedHello = false;
        isConnecting = false;
        sessionId = null;
        sn = 0;
        
        try {
            if (isOpen()) {
                close();
            }
        } catch (Exception e) {
            log.error("关闭连接时发生错误", e);
        }
        
        // 通知外部处理重连
        if (reconnectAware != null) {
            new Thread(() -> {
                try {
                    // 等待一小段时间确保连接完全关闭
                    Thread.sleep(1000);
                    reconnectAware.onReconnectRequired();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.error("重连等待被中断", e);
                }
            }).start();
        }
    }

    private void startHeartbeat() {
        clearTimers(); // 确保之前的定时器被清除
        
        lastPongTime = System.currentTimeMillis();
        
        // 心跳发送定时器
        heartbeatTimer = new Timer();
        heartbeatTimer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                try {
                    if (isOpen()) {
                        // 发送心跳包
                        String pingMessage = objectMapper.writeValueAsString(new HeartbeatMessage());
                        send(pingMessage);
                        long currentTime = System.currentTimeMillis();
                        log.debug("发送心跳包，间隔: {}ms", currentTime - lastPongTime);
                        
                        // 检查上一次pong响应时间
                        if (currentTime - lastPongTime > kookProperties.getWebsocket().getHeartbeatInterval() * 2) {
                            log.warn("心跳超时（超过两个心跳间隔未收到响应），准备重连 - 当前时间: {}, 上次pong时间: {}, 超时时间: {}ms", 
                                currentTime, lastPongTime, currentTime - lastPongTime);
                            handleReconnect();
                        }
                    } else {
                        log.warn("连接已关闭，无法发送心跳包");
                        handleReconnect();
                    }
                } catch (Exception e) {
                    log.error("发送心跳包时发生错误", e);
                    handleReconnect();
                }
            }
        }, kookProperties.getWebsocket().getHeartbeatInterval(), kookProperties.getWebsocket().getHeartbeatInterval());
    }

    private void clearTimers() {
        Timer[] timers = {heartbeatTimer, helloTimeoutTimer, connectionTimeoutTimer};
        for (Timer timer : timers) {
            if (timer != null) {
                timer.cancel();
            }
        }
        heartbeatTimer = null;
        helloTimeoutTimer = null;
        connectionTimeoutTimer = null;
        lastPongTime = 0;
    }

    private void stopHeartbeat() {
        clearTimers();
    }

    private volatile boolean hasReceivedHello = false;

    @Override
    public void onClose(int code, String reason, boolean remote) {
        log.info("WebSocket连接已关闭 - 代码: {}, 原因: {}, 远程: {}", code, reason, remote);
        clearTimers();
        
        // 如果是在连接过程中关闭，或者是已经建立的连接被远程关闭，则触发重连
        if (isConnecting || (remote && hasReceivedHello)) {
            handleReconnect();
        } else {
            // 重置状态
            hasReceivedHello = false;
            isConnecting = false;
            sessionId = null;
            sn = 0;
        }
    }

    @Override
    public void onError(Exception ex) {
        log.error("WebSocket发生错误", ex);
        handleReconnect();
    }

    public String getSessionId() {
        return sessionId;
    }

    public int getSn() {
        return sn;
    }

    private static class HeartbeatMessage {
        private final int s = 2; // 心跳信号

        public int getS() {
            return s;
        }
    }
}